package metamodel;

import antlr.RecognitionException;
import antlr.TokenStreamException;
import metamodel.coreservices.CloneTransformer;
import metamodel.coreservices.Deserializer;
import metamodel.coreservices.Serializer;
import metamodel.coreservices.SimpleComparator;
import metamodel.transformer.BottomUpTransformingGraphTraverser;
import metamodel.visitor.ObjectGraphIterator;
import metamodel.visitor.ObjectGraphVisitorBaseImpl;
import metamodel.parser.TraversalLexer;
import metamodel.parser.TraversalParser;
import metamodel.parser.ReferenceResolver;

import java.io.*;
import java.util.Collection;


/**
 * User: felix
 * Date: 11.06.2005
 */
public class TraversalUtils {
    /** */
    public static void print(ClassNode classNode, Object data, final Writer out) {
        final PrintWriter pout = new PrintWriter(out);
        pout.println(classNode.getTypeName());

        ObjectGraphIterator it = new ObjectGraphIterator(classNode, new ObjectGraphVisitorBaseImpl() {

            String level = "\t";

            @Override
            public void visitCollection(CollectionRef property, Object value, Object parent) {
                pout.println(level + property.getName() + " : ");
                level += "\t";
            }

            public void visitObject(ClassNode classNode, DeepProperty property, Object value, Object parent) {
                String prefix;
                if (property instanceof CollectionRef) {
                    prefix = "";
                } else {
                    prefix = property.getName() + " : ";
                }
                pout.println(level + prefix + classNode.getTypeName());
                level += "\t";

            }


            public void visitFlatProperty(FlatProperty property, Object value, Object parent, boolean hasNext) {
                pout.println(level + property.getName() + ": " + value);
            }

            public void endObject(ClassNode classNode, DeepProperty dp, Object data, Object parent, boolean hasNext) {
                level = level.substring(0, level.length() - 1);
            }

            @Override
            public void endCollection(CollectionRef collRef, Collection data, Object parent, boolean hasNext) {
                level = level.substring(0, level.length() - 1); 
            }

            public void endClassNode(ClassNode typeName, Object data) {

            }

            public void visitCyclic(ClassNode classNode, DeepProperty property, Object value, Object parent, boolean hasNext) {
                pout.println(level + "@cyclic reference to " + property.getValue(value));

            }

        }, ObjectGraphIterator.TraversalMode.NOTIFY_CYCLES);
        it.iterate(data);
        try {
            out.close();
        } catch (IOException e) {
            throw new RuntimeException(e);
        }
    }

    public static Object clone(ClassNode classNode, Object data) {
        return new BottomUpTransformingGraphTraverser(classNode, new CloneTransformer()).transform(data);

    }

    public static boolean equals(ClassNode classNode, Object object1, Object object2) {
        SimpleComparator sc = new SimpleComparator(classNode);
        boolean equals = sc.equals(object1, object2);
        return equals;
    }

    public static ClassNode parse(String expr) throws RecognitionException, TokenStreamException {
        StringReader reader = new StringReader(expr);
        ClassNode classNode = parse(reader);
        return classNode;
    }

    public static ClassNode parse(Reader reader) throws RecognitionException, TokenStreamException {
        TraversalLexer lexer = new TraversalLexer(reader);
        TraversalParser parser = new TraversalParser(lexer);
        ClassNode classNode = parser.traversalDescription();
        new ReferenceResolver(classNode);
        return classNode;
    }

    public static ClassNode parse(InputStream stream) throws TokenStreamException, RecognitionException {
        return parse(new InputStreamReader(stream));
    }

    public static String toString(ClassNode meta, Object model) {
        StringWriter bout = new StringWriter();
        print(meta, model, bout);
        return bout.getBuffer().toString();
    }

    public static ClassNode createMetaModel() throws RecognitionException, TokenStreamException, IOException {
        InputStream stream = TraversalUtils.class.getResourceAsStream("metamodel.txt");
        final ClassNode classNode = parse(stream);
        stream.close();
        return classNode;


    }

    public static String serialize(ClassNode meta, Object object) {
        StringWriter writer = new StringWriter();
        Serializer ser = new Serializer(meta, writer);
        ser.serialize(object);
        return writer.toString();
    }


    public static String serialize(ClassNode meta, Collection objects) {
        StringWriter writer = new StringWriter();
        Serializer ser = new Serializer(meta, writer);
        ser.serialize(objects);
        return writer.toString();
    }


    public static Object deserialize(ClassNode meta, String serial) throws IOException {
        StringReader r = new StringReader(serial);
        Deserializer d = new Deserializer(meta, r);
        return d.deserialize();

    }


    public static Collection deserializeCollection(ClassNode meta, String serial) throws IOException {
        StringReader r = new StringReader(serial);
        Deserializer d = new Deserializer(meta, r);
        return d.deserializeCollection();

    }
}
